package org.wattdepot.cli.command;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.util.tstamp.Tstamp;

/**
 * Implements the power stats command for the command line interface.
 * 
 * @author George Lee
 *
 */
public class PowerStatsCommand extends WattDepotCliCommand {

  /**
   * Creates a new instance of the command handler.
   * 
   * @param client An instance of the WattDepotClient.
   */
  public PowerStatsCommand(WattDepotClient client) {
    super(client);
  }

  /**
   * Gets the average/max/min power consumed/generated by a source on a given date.
   * Returns an error message if one occurs.
   * @see org.wattdepot.cli.command.WattDepotCliCommand#doCommand(String)
   * 
   * @param command The command to process.
   * @return The result of the command or an error message if one occurs.
   */
  @Override
  public String doCommand(String command) {
    XMLGregorianCalendar day;
    PowerCommandType type = PowerCommandType.POWER_GENERATED;
    StatisticType statType = StatisticType.STAT_MAX;
    String source;
    int interval;
    
    StringTokenizer tokenizer = new StringTokenizer(command);
    List<String> tokens = new ArrayList<String>();
    while (tokenizer.hasMoreTokens()) {
      tokens.add(tokenizer.nextToken());
    }
    
    if (tokens.size() != 9) {
      return this.reportError(PARAMETER_COUNT_ERROR);
    }
    //Check for invalid parameters
    if (!"powerstats".equals(tokens.get(0))) {
      return WattDepotCliCommand.reportUnknownCommand(command);
    }
    
    if (!"day".equals(tokens.get(3))) {
      return this.reportError(PARAMETER_ERROR + tokens.get(3));
    }
    
    if (!"sampling-interval".equals(tokens.get(5))) {
      return this.reportError(PARAMETER_ERROR + tokens.get(5));
    }
    
    if (!"statistic".equals(tokens.get(7))) {
      return this.reportError(PARAMETER_ERROR + tokens.get(7));
    }
    
    if ("consumed".equals(tokens.get(1))) {
      type = PowerCommandType.POWER_CONSUMED;
    }
    
    else if (!"generated".equals(tokens.get(1))) {
      return this.reportError(PARAMETER_ERROR + tokens.get(2));
    }
    
    source = tokens.get(2);
    
    try {
      day = Tstamp.makeTimestamp(tokens.get(4));
    }
    catch (Exception e) {
      return this.reportError(TSTAMP_ERROR + tokens.get(4));
    }    
    
    try {
      interval = Integer.valueOf(tokens.get(6));
      if (interval <= 0) {
        return this.reportError(INTERVAL_ERROR + tokens.get(6));
      }
    }
    catch (NumberFormatException e) {
      return this.reportError(INTERVAL_ERROR + tokens.get(6));
    }
    
    if ("min".equals(tokens.get(8))) {
      statType = StatisticType.STAT_MIN;
    }
    else if ("average".equals(tokens.get(8))) {
      statType = StatisticType.STAT_AVG;
    }
    else if (!"max".equals(tokens.get(8))) {
      return this.reportError(PARAMETER_ERROR + tokens.get(8));
    }
    
    return this.getPowerForDay(type, source, day, interval, statType);
  }

  /**
   * Gets the average/max/min power consumed/generated by a source on a given date.
   * 
   * @param type Whether we should look at the power generated or the power consumed.
   * @param source The power source to query.
   * @param start The day to process.
   * @param interval The amount of minutes to sample our data over.
   * @param statType Whether we should get the max, min, or average.
   * 
   * @return The result of the operation or an error message if one should occur.
   */
  private String getPowerForDay(PowerCommandType type, String source, XMLGregorianCalendar start,
      int interval, StatisticType statType) {
    int count = 0;
    double power = 0, minPower = Double.MAX_VALUE, maxPower = Double.MIN_VALUE;
    String result;
    
    XMLGregorianCalendar timestamp, end;
    timestamp = start;
    end = Tstamp.incrementDays(start, 1);
    
    try {
      while (Tstamp.lessThan(timestamp, end)) {
        count++;
        if (type == PowerCommandType.POWER_GENERATED) {
          power = this.client.getPowerGenerated(source, timestamp);
        }
        else {
          power = this.client.getPowerConsumed(source, timestamp);
        }

        if (power < minPower) {
          minPower = power;
        }
        if (power > maxPower) {
          maxPower = power;
        }
        timestamp = Tstamp.incrementMinutes(timestamp, interval);
      }

      if (statType == StatisticType.STAT_AVG) {
        Double average = power / count;
        result = average.toString();
      }
      else if (statType == StatisticType.STAT_MAX) {
        result = Double.valueOf(maxPower).toString();
      }
      else {
        result = Double.valueOf(minPower).toString();
      }
    }
    catch (BadXmlException e) {
      result = this.reportError(BAD_XML_ERROR);
    }
    catch (NotAuthorizedException e) {
      result = this.reportError(CONNECTION_ERROR);
    }
    catch (ResourceNotFoundException e) {
      result = this.reportError(SOURCE_ERROR + source);
    }
    catch (MiscClientException e) {
      result = this.reportError(CONNECTION_ERROR);
    }
    
    return result;
  }

  /**
   * Gets the help string associated with the power stats command.
   * 
   * @see org.wattdepot.cli.command.WattDepotCliCommand#getHelp()
   * @return Information on how to invoke the power stats command.
   */
  @Override
  public String getHelp() {
    return "powerstats [generated|consumed] {source} day {day} sampling-interval {minutes} "
      + "statistic {max|min|average}\n"
      + "  Returns the maximum, minimum, or average power generated"
      + " by the source for the day.\n";
  }
}
